#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 时间     : 2019-08-02 00:01:11
# 作者     : DL (584054432@qq.com)
# 网址     : https://gitee.com/dingliangPython/
# 软件版本 : Python3.7.3
# 功能     ： BUS SDK 通信模块
import time
import os
from queue import Queue
from ctypes import c_char, c_char_p, c_int, c_void_p, c_double, byref, cast, c_longlong, Structure, CFUNCTYPE, POINTER, CDLL
from struct import unpack, pack
import configparser


import logging
import tools
log=logging.getLogger("main.sdk")
# 装饰器
def method(prototype):
    class MethodDescriptor(object):

        def __init__(self, func):
            self.func = func
            self.bound_funcs = {}  # hold on to references to prevent gc

        def __get__(self, obj, type=None):
            assert obj is not None  # always require an instance
            try:
                return self.bound_funcs[obj, type]
            except KeyError:
                ret = self.bound_funcs[obj, type] = prototype(
                    self.func.__get__(obj, type))
                return ret
    return MethodDescriptor

# 遥信结构体


class GCL_YX_T(Structure):
    _fields_ = [
        ('address', c_int),
        ('value', c_int),
        ('quality', c_int),
        ('datetime', c_longlong)
    ]
# 遥测结构体


class GCL_YC_T(Structure):
    _fields_ = [
        ('address', c_int),
        ('value', c_double),
        ('quality', c_int),
        ('datetime', c_longlong)
    ]
# 遥文结构体


class GCL_YW_T(Structure):
    _fields_ = [
        ('address', c_int),
        ('value', c_char * 128),
        ('quality', c_int),
        ('datetime', c_longlong)
    ]
# 结构体


class GCL_PSM_ATTACH(Structure):
    _fields_ = [
        ('reason', c_int),
        ('containerId', c_int),
        ('sourceId', c_int),
        ('targetId', c_int),
        ('tag', c_int)
    ]

CALLBACK_SDK_ERR = method(CFUNCTYPE(None, c_int))
CALLBACK_SDK_MSG = method(
    CFUNCTYPE(None, c_char_p, c_char_p, c_int, POINTER(GCL_PSM_ATTACH)))
CALLBACK_SDK_FILE = method(CFUNCTYPE(None, c_char_p, POINTER(GCL_PSM_ATTACH)))
CALLBACK_SDK_RT_REQ = method(CFUNCTYPE(None, POINTER(GCL_PSM_ATTACH)))
CALLBACK_SDK_RT_REV = method(
    CFUNCTYPE(None, c_int, c_void_p, c_int, c_int, POINTER(GCL_PSM_ATTACH)))

# SDK


class GCL_SDK:

    def __init__(self):
        self.sdk_path="./gcl_sdk1.dll"
        if not os.path.exists(self.sdk_path):
            tools.Set_Message("文件夹：{}，缺少gcl_sdk1.dll文件,不能初始化SDK,即将退出".format(os.path.abspath('./')))
            os._exit(0)
        self.lib = CDLL(os.path.abspath(self.sdk_path))
    def reg_err_notify(self, fn):
        return self.lib.gci_register_error_notify(fn)

    def unreg_err_notify(self, fn):
        return self.lib.gci_unregister_error_notify(fn)

    def reg_msg_cmd_notify(self, fn):
        return self.lib.gci_register_message_command_notify(fn)

    def unreg_msg_cmd_notify(self, fn):
        return self.lib.gci_unregister_message_command_notify(fn)

    def reg_file_transfer_notify(self, fn):
        return self.lib.gci_register_file_transfer_notify(fn)

    def unreg_file_transfer_notify(self, fn):
        return self.lib.gci_unregister_file_transfer_notify(fn)

    def reg_file_transfer_result_notify(self, fn):
        return self.lib.gci_register_file_transfer_result_notify(fn)

    def unreg_file_transfer_result_notify(self, fn):
        return self.lib.gci_unregister_file_transfer_result_notify(fn)

    def reg_rt_rev_notify(self, fn):
        return self.lib.gci_register_realtime_data_post_notify(fn)

    def unreg_rt_rev_notify(self, fn):
        return self.lib.gci_unregister_realtime_data_post_notify(fn)

    def init(self, argc, argv):
        return self.lib.gci_init(argc, argv)

    def uninit(self):
        return self.lib.gci_cleanup()

    def send_msg(self, cmd, pram, lenth, attach):
        return self.lib.gci_message_command_send(cmd, pram, lenth, attach)

    def send_yx(self, e):
        buff = pack('=iiiq', e.address, e.value, e.quality, e.datetime)
        return self.lib.gci_realtime_data_post(0x01010203, buff, 20, 1, None)

    def send_yc(self, e):
        buff = pack('=idiq', e.address, e.value, e.quality, e.datetime)
        return self.lib.gci_realtime_data_post(0x0101021C, buff, 24, 1, None)

    def send_yw(self, e):
        buff = pack('=i128siq', e.address, e.value, e.quality, e.datetime)
        return self.lib.gci_realtime_data_post(0x0101022F, buff, 144, 1, None)

    def send_yx_array(self, v):
        if len(v) > 0:
            buff = b''
            for e in v:
                buff += (pack('=iiiq', e.address, e.value, e.quality, e.datetime))
            return self.lib.gci_realtime_data_set_list(0x01010203, buff, 20 * len(v), len(v), None)
        return None

    def send_yc_array(self, v):
        if len(v) > 0:
            buff = b''
            for e in v:
                buff += (pack('=idiq', e.address, e.value, e.quality, e.datetime))
            return self.lib.gci_realtime_data_set_list(0x0101021C, buff, 24 * len(v), len(v), None)
        return None

    def send_yw_array(self, v):
        if len(v) > 0:
            buff = b''
            for e in v:
                buff += (pack('=i128siq', e.address,
                              e.value, e.quality, e.datetime))
            return self.lib.gci_realtime_data_set_list(0x0101022F, buff, 144 * len(v), len(v), None)

        return None
# 回调


class SDK_CB:

    def __init__(self, obj):
        self.parent = obj
        self.realTimeDatas = {}
        self.realTimeDataMessage = Queue()

    def set_callbacks(self):
        # 注册消息回调
        self.parent.sdk.reg_msg_cmd_notify(self.cb_sdk_msg)
        # 注册实时回调
        self.parent.sdk.reg_rt_rev_notify(self.cb_sdk_rt_rev)
        # 注册文件回调
        self.parent.sdk.reg_file_transfer_result_notify(self.cb_sdk_Filr)

    @CALLBACK_SDK_ERR
    def cb_sdk_error(self, err):
        log.error("异常：{}".format(err))

    @CALLBACK_SDK_MSG
    # 消息回调
    def cb_sdk_msg(self, cmd, pram, length, attach):
        pram = pram.decode('utf-8')
        cmd = cmd.decode('utf-8')
        self.realTimeDataMessage.put((cmd, pram))
    # 文件回调

    @CALLBACK_SDK_FILE
    def cb_sdk_Filr(self, file, attach):

        print('文件:', file, attach)

    @CALLBACK_SDK_RT_REQ
    def cb_sdk_rt_req(self, attach):
        # print(attach)
        pass
    # 实时回调

    @CALLBACK_SDK_RT_REV
    def cb_sdk_rt_rev(self, rt_rtype, data, size, count, attach):
        x = cast(data, POINTER(c_char * size))
        # 报文
        x = bytes([v[0] for v in x.contents])
        # 长度
        step = int(size / count)
        if rt_rtype == 0x01010203 and count > 0:
            for i in range(0, size, step):
                value = unpack('=iiiq', x[i: i + step])
                value = list(value)
                value[3] = value[3] / 1000
                self.realTimeDatas.update({value[0]: value})

        elif rt_rtype == 0x0101021C and count > 0:
            for i in range(0, size, step):
                value = unpack('=idiq', x[i: i + step])
                value = list(value)
                value[3] = value[3] / 1000
                self.realTimeDatas.update({value[0]: value})

        elif rt_rtype == 0x0101022F and count > 0:
            for i in range(0, size, step):
                value = unpack('=i128siq', x[i: i + step])
                value = list(value)
                value[3] = value[3] / 1000
                try:
                    value[1] =value[1].decode('utf','ignore').replace('\x00','')
                except:
                    pass
                self.realTimeDatas.update({value[0]: value})
# SDK实时数据封装


class sdk_Client(object):
    """docstring for sdk_Client"""

    def __init__(self):
        super(sdk_Client, self).__init__()
        # self.log=logging.getLogger("main.sdk")
        # SDK实例
        self.sdk = GCL_SDK()
        # 回调实例
        self.sdk_cb = SDK_CB(self)
        # 注册回调
        self.sdk_cb.set_callbacks()
        # 初始化Sdk
        self.init_sdk()
        self.yx = GCL_YX_T()
        self.yc = GCL_YC_T()
        self.yw = GCL_YW_T()
        self.configInfo = self.get_sdk_config()
    @tools.try_error_class
    def get_sdk_config(self):
        path = "./gcl_sdk/config/apps.config.ini"
        if not os.path.exists(path):
            tools.Set_Message("没有找到配置文件：{},不能初始化SDK".format(path))
        ini = configparser.ConfigParser()
        config = ini.read(path,'gbk')
        log.info("BusSDK初始化成功。。。。")
        return config
    # 初始化Sdk

    def init_sdk(self):
        pStr = c_char_p()
        sTemp = os.path.abspath('.') + "\\"
        pStr.value = sTemp.encode('gbk')
        self.sdk.init(1, byref(pStr))
    # 结束
    def close(self):
        return self.sdk.uninit()
    # 读单个值
    def get_measure(self, measureId):
        if not measureId:
            return
        return self.sdk_cb.realTimeDatas.get(int(measureId), None)
    # 写单个值

    def set_measure(self, measureId, value):
        measureId=str(int(measureId))
        if not measureId:
            log.warning(f"not send data !!!,check send data : measure={measureId},value={value}")
            return
        quality = 1
        curTime = int(time.time() * 1000)
        # 遥信
        if measureId[0] == '1':
            self.yx.address = int(measureId)
            self.yx.value = int(value)
            self.yx.quality = quality
            self.yx.datetime = curTime
            sendResult = None
            for i in range(5):
                sendResult = self.sdk.send_yx(self.yx)
                if sendResult != -1:
                    break
            return True if sendResult != -1 else None
        # 遥测
        elif measureId and hex(int(measureId))[2] == '2':
            self.yc.address = int(measureId)
            self.yc.value = float(value)
            self.yc.quality = quality
            self.yc.datetime = curTime
            sendResult = None
            for i in range(5):
                sendResult = self.sdk.send_yc(self.yc)
                if sendResult != -1:
                    break
            return True if sendResult != -1 else None
        # 遥文
        elif measureId and hex(int(measureId))[2] == '3':
            self.yw.address = int(measureId)
            self.yw.value =  value.encode('utf-8')
            self.yw.quality = quality
            self.yw.datetime = curTime
            sendResult = None
            for i in range(5):
                sendResult = self.sdk.send_yw(self.yw)
                if sendResult != -1:
                    break
            return True if sendResult != -1 else None
    # 写多个值

    def set_measure_list(self, valueList):
        if not valueList:
            log.warning("不能写多个MID值，请检测写数据列表")
            return
        quality = 1
        curTime = int(time.time() * 1000)
        yx = []
        yc = []
        yw = []
        yxSendResult = None
        ycSendResult = None
        ywSendResult = None
        # 组合数据
        for row in valueList:
            measureId = row[0]
            value = row[1]
            # 遥信
            if measureId[0] == '1':
                self.yx.address = int(measureId)
                self.yx.value = int(value)
                self.yx.quality = quality
                self.yx.datetime = curTime
                yx.append(self.yx)
            # 遥测
            elif measureId[0] == '3':
                self.yc.address = int(measureId)
                self.yc.value = float(value)
                self.yc.quality = quality
                self.yc.datetime = curTime
                yc.append(self.yc)
            # 遥文
            elif measureId[0] == '5':
                e = GCL_YW_T()
                e.address = int(measureId)
                e.value = value.encode('utf-8')
                e.quality = quality
                e.datetime = curTime
                yw.append(e)
        if yx:
            for i in range(5):
                yxSendResult = self.sdk.send_yx_array(yx)
                if yxSendResult != -1:
                    break
        if yc:
            for i in range(5):
                ycSendResult = self.sdk.send_yc_array(yc)
                if ycSendResult != -1:
                    break
        if yw:
            for i in range(5):
                ywSendResult = self.sdk.send_yw_array(yw)
                if ywSendResult != -1:
                    break
        return yxSendResult, ycSendResult, ywSendResult
    # 读取消息

    def get_message(self):
        if self.sdk_cb.realTimeDataMessage.qsize() >= 1:
            command, message = self.sdk_cb.realTimeDataMessage.get()
            log.debug("接收到控制，命令：{}，信息：{}".format(command,message))
            if command.split(".")[1] in("yk", "yt", "ys", "yx", "yc", "yw"):
                try:
                    vDict=dict([s.split("=")  for s in message.split(";") if len(s)>1])
                    return vDict["measure"],vDict["value"]
                except Exception as e:
                    log.warning("解析控制命令失败：{},消息：{}，异常：{}".format(command,message,e))
                    return False,False
            else:
                # log.info("接收的是一个消息,命令：{},消息：{}".format(command,message))
                return command, message

    # 发送消息
    def set_message(self, cmd=1, message=""):
        if cmd == 1:
            cmd = 'send.yk.1'.encode("gbk")
        elif cmd == 2:
            cmd = 'send.yt.1'.encode("gbk")
        elif cmd == 3:
            cmd = 'send.ys.1'.encode("gbk")
        else:
            cmd = cmd.encode("gbk")

        msg = str(message).encode("gbk")
        sendResult = self.sdk.send_msg(cmd, msg, len(msg), None)
        return True if sendResult >= 0 else None


if __name__ == '__main__':
    import random
    app = sdk_Client()

    while 1:
        # x=app.get_measure(16777220)
        # print("取值",x)
        # xx = app.get_message()
        # print("取消息", xx)
        # r=app.set_measure('16777226',random.randint(0,255))
        # print("yx",r)
        # r=app.set_measure('33554432',random.random())
        # print("yc",r)
        # r=app.set_measure('50331649',random.choice('abcdefghijklmnopqrstuvwxyz!@#$%^&*()'))
        # print("yw",r)

        # r=app.set_measure_list(
        #     [   ('16777228',random.randint(0,255)),
        #         ('33554434',random.random()),
        #         ('50331651',random.choice('abcdefghijklmnopqrstuvwxyz!@#$%^&*()')),
        #         ('50331652',random.choice('abcdefghijklmnopqrstuvwxyz!@#$%^&*()'))
        #     ]
        #     )
        # print("3Y_list",r)
        r = app.set_message(1, "measure=16777481;value=33;")
        # f=r'D:\CODE\PYTHON\项目数据\铜陵\中间报文处理\20200423000840856_00201.efl'
        # xx=app.sdk_cb.cb_sdk_Filr(f)
        # print("消息", r)
        print('发送文件',r)
        time.sleep(5)
        print(time.time(), "\n")
